﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using TheVerge.Phone.Controls;
using Microsoft.Phone.Tasks;
using Microsoft.Xna.Framework.Media;
using System.Windows.Media.Animation;

namespace TheVerge.Phone.Pages
{
    public partial class GalleryPage : BasePage
    {
        private volatile bool _CanNotManipulate;
        
        private List<string> _Images;
        private int _Index;
        
        private double LEFT_START;
        private double RIGHT_START;

        private bool _IsZooming;
        private double _ImageScale = 1d;
        private Point _ImageTranslation = new Point(0, 0);
        private Point _FingerOne;
        private Point _FingerTwo;
        private double _PreviousScale;

        private TimeSpan TrasitionDuration = TimeSpan.FromMilliseconds(225D);

        public GalleryPage()
        {
            InitializeComponent();

            AppBarHelper.SaveImageAction = SaveImage;
            ApplicationBar.Opacity = 0.5D;

            var dt = App.MainVM.ActiveGallery;

            if (dt == null) return;

            SetImages(dt.Photos.Select(p => p.Full));
        }

        #region OverRides
        protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
        {
            base.OnBackKeyPress(e);


            if (_IsZooming)
            {
                ResetZoom();
                e.Cancel = true;
            }
        }


        protected override IEnumerable<IApplicationBarMenuItem> MenuItems
        {
            get { yield return AppBarHelper.SaveImageItem; }
        }
        
        protected override bool IsMini { get { return true; } }

        protected override IEnumerable<IApplicationBarIconButton> Buttons { get { return new IApplicationBarIconButton[0]; } }
        #endregion

        #region Event Handlers
        private void This_DoubleTab(object sender, GestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            if (_IsZooming)
            {
                ResetZoom();
            }
            else
            {
                _IsZooming = true;

                var point = e.GetPosition(center);
                point.X = -point.X;
                point.Y = -point.Y;

                UpdatePicture(2D, point);
            }
        }

        private void This_DragCompleted(object sender, DragCompletedGestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            if (_IsZooming) return;

            _CanNotManipulate = true;

            //-- We want to transition to another image the image was moved 17.6% in either direction.
            var threshold = ActualWidth * 0.176D;
            var absDistance = Math.Abs(centerTransform.TranslateX);

            if (absDistance > threshold)
            {
                if (centerTransform.TranslateX > 0)
                    MovePrevious();
                else
                    MoveNext();
            }
            else
                AnimateHome();
        }

        private void This_DragDelta(object sender, DragDeltaGestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            
            if (_IsZooming)
            {
                var newX = centerTransform.TranslateX + (e.HorizontalChange * _ImageScale * .76);
                var newY = centerTransform.TranslateY + (e.VerticalChange * _ImageScale * .76);
                var p = new Point(newX, newY);

                UpdatePicture(_ImageScale, p);

                return;
            }
            if (e.Direction == System.Windows.Controls.Orientation.Horizontal)
            {
                leftTransform.TranslateX += e.HorizontalChange;
                centerTransform.TranslateX += e.HorizontalChange;
                rightTransform.TranslateX += e.HorizontalChange;
            }
        }

        private void This_Loaded(object sender, RoutedEventArgs e)
        {
            InitializeDimensions();
        }

        private void This_OrientationChanged(object sender, OrientationChangedEventArgs e)
        {
            InitializeDimensions();
        }

        private void This_PinchStarted(object sender, PinchStartedGestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            _FingerOne = e.GetPosition(center, 0);
            _FingerTwo = e.GetPosition(center, 1);
            _PreviousScale = 1;
            _IsZooming = true;
        }

        private void This_PinchDelta(object sender, PinchGestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            var newScale = e.DistanceRatio / _PreviousScale;

            if (newScale > 4D) return;

            var currentFingerOne = e.GetPosition(center, 0);
            var currentFingerTwo = e.GetPosition(center, 1);
            var translationDelta = GetTranslationOffset(currentFingerOne,
            currentFingerTwo, _FingerOne, _FingerTwo, _ImageTranslation, newScale);

            _FingerOne = currentFingerOne;
            _FingerTwo = currentFingerTwo;
            _PreviousScale = e.DistanceRatio;

            var scale = _ImageScale * newScale;

            var x = _ImageScale > 1D ? _ImageTranslation.X + translationDelta.X : (center.ActualWidth - (center.ActualWidth * scale)) / 2D;
            var y = _ImageScale > 1D ? _ImageTranslation.Y + translationDelta.Y : (center.ActualHeight - (center.ActualHeight * scale)) / 2D;

            UpdatePicture(scale, new Point(x, y));
        }

        private void This_PinchCompleted(object sender, PinchGestureEventArgs e)
        {
            if (_CanNotManipulate) return;

            if (centerTransform.ScaleX > 1)
                _IsZooming = true;
            else
            {
                ResetZoom();
            }
        }

        #endregion

        #region Helper Methods
        private void Animate(CompositeTransform left, double leftX, CompositeTransform center, double centerX, CompositeTransform right, double rightX, Action storyboardCompleted)
        {
            _CanNotManipulate = true;
            Storyboard sb = new Storyboard();
            CreateAnimation(sb, left, leftX);
            CreateAnimation(sb, center, centerX);
            CreateAnimation(sb, right, rightX);
            sb.Completed += (s, e) => 
            {
                storyboardCompleted();
                _CanNotManipulate = false;
            };
            sb.Begin();
        }

        private void AnimateHome()
        {
            Animate(leftTransform, LEFT_START, centerTransform, 0D, rightTransform, RIGHT_START, () => { });
        }

        private double Coerce(double value, double actual, double scale)
        {
            var min = -(actual * scale - actual);
            var max = 0;

            if (value > max) return max; //-- Too high
            else if (value < min) return min; //-- Too low

            return value;
        }

        private double CoerceVertical(double y, double scale)
        {
            return Coerce(y, center.ActualHeight, scale);
        }

        private double CoerceHorizontal(double x, double scale)
        {
            return Coerce(x, center.ActualWidth, scale);
        }

        private double CoerceScale(double scale)
        {
            if (scale > 4D) return 4D;
            if (scale < 0.25D) return 0.25D;

            return scale;
        }

        private void Clear()
        {
            left.Source = right.Source = center.Source = null;
        }

        private void CreateAnimation(Storyboard sb, DependencyObject obj, double x)
        {
            var animation = new DoubleAnimation();
            animation.To = x;
            animation.Duration = TrasitionDuration;
            sb.Children.Add(animation);
            Storyboard.SetTarget(animation, obj);
            Storyboard.SetTargetProperty(animation, new PropertyPath(CompositeTransform.TranslateXProperty));
        }

        private Point GetTranslationOffset(Point currentFingerOne, Point currentFingerTwo, Point oldFingerOne, Point oldFingerTwo, Point currentPosition, double scale)
        {
            var newFingerOnePosition = new Point(
                currentFingerOne.X + (currentPosition.X - oldFingerOne.X) * scale,
                currentFingerOne.Y + (currentPosition.Y - oldFingerOne.Y) * scale);
            var newFingerTwoPosition = new Point(
                currentFingerTwo.X + (currentPosition.X - oldFingerTwo.X) * scale,
                currentFingerTwo.Y + (currentPosition.Y - oldFingerTwo.Y) * scale);
            var newPosition = new Point(
                (newFingerOnePosition.X + newFingerTwoPosition.X) / 2,
                (newFingerOnePosition.Y + newFingerTwoPosition.Y) / 2);
            return new Point(
                newPosition.X - currentPosition.X,
                newPosition.Y - currentPosition.Y);
        }

        private void InitializeDimensions()
        {
            centerTransform.TranslateX = 0D;
            LEFT_START = leftTransform.TranslateX = -(ActualWidth);
            RIGHT_START = rightTransform.TranslateX = ActualWidth;
            left.Width = center.Width = right.Width = ActualWidth;
        }

        private void MoveNext()
        {
            if (_Index >= (_Images.Count - 1))
            {
                AnimateHome();
                return;
            }

            Animate(leftTransform, ActualWidth * -2D, centerTransform, ActualWidth * -1, rightTransform, 0, () =>
            {
                _Index++;
                left.Source = center.Source;
                center.Source = right.Source;

                var rightIndex = _Index + 1;

                if (rightIndex < _Images.Count)
                    right.Source = new BitmapImage(new Uri(_Images[rightIndex]));
                else
                    right.Source = null;

                InitializeDimensions();
            });
        }

        private void MovePrevious()
        {
            if (_Index == 0)
            {
                AnimateHome();
                return;
            }

            Animate(leftTransform, 0D, centerTransform, ActualWidth, rightTransform, ActualWidth * 2D, () =>
            {
                 _Index--;
                right.Source = center.Source;
                center.Source = left.Source;

                var leftIndex = _Index - 1;

                if (leftIndex > -1)
                    left.Source = new BitmapImage(new Uri(_Images[leftIndex]));
                else
                    left.Source = null;

                InitializeDimensions();
            });
        }

        private void ResetTranslations()
        {
            centerTransform.TranslateX = 0;
            leftTransform.TranslateX = LEFT_START;
            rightTransform.TranslateX = RIGHT_START;
        }

        private void ResetZoom()
        {
            _ImageScale = 1D;
            centerTransform.ScaleX = centerTransform.ScaleY = 1;
            centerTransform.TranslateX = centerTransform.TranslateY = 0;
            _ImageTranslation = new Point(0, 0);
            _IsZooming = false;
        }

        private void SetImages(IEnumerable<string> images)
        {
            _Images = new List<string>();
            foreach (var i in images)
            {
                _Images.Add(i);
            }


            center.Source = left.Source = right.Source = null;
            InitializeDimensions();

            if (_Images.Count > 0)
                center.Source = new BitmapImage(new Uri(_Images[0]));

            if (_Images.Count > 1)
                right.Source = new BitmapImage(new Uri(_Images[1]));
        }

        private void UpdatePicture(double scaleFactor, Point p)
        {
            centerTransform.ScaleX = centerTransform.ScaleY = _ImageScale = CoerceScale(scaleFactor);

            if (_ImageScale > 1)
            {
                centerTransform.TranslateX = _ImageTranslation.X = CoerceHorizontal(p.X, _ImageScale);
                centerTransform.TranslateY = _ImageTranslation.Y = CoerceVertical(p.Y, _ImageScale);
            }
            else
            {
                //-- This will center the image in the frame instead of being in the top left corner.
                centerTransform.TranslateX = _ImageTranslation.X = (center.ActualWidth - (center.ActualWidth * _ImageScale)) / 2D;
                centerTransform.TranslateY = _ImageTranslation.Y = (center.ActualHeight - (center.ActualHeight * _ImageScale)) / 2D;
            }
        }
        #endregion

        #region App Bar Actions
        private void SaveImage()
        {
            var imageUri = _Images[_Index];

            WebClient client = new WebClient();
            client.OpenReadCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    MediaLibrary library = new MediaLibrary();
                    var pic = library.SavePicture(@"TheVerge_Image_" + DateTime.Now, e.Result);

                    MessageBox.Show("Image was saved to your photo hub (Saved Pictures).", "Save Success", MessageBoxButton.OK);
                }
                else
                {
                    MessageBox.Show("There was a problem saving the image.");
                }
            };
            client.OpenReadAsync(new Uri(imageUri, UriKind.Absolute));
        }
        #endregion
    }
}